package de.pass.ch.graph.node;

import static java.lang.Integer.*;
import static de.pass.ch.util.Constants.LAST_EDGE_LIMIT;

import java.io.Serializable;
import java.util.Arrays;

import de.pass.ch.graph.edge.Edge;

public class UpNode implements INode, Serializable {

	private static final long serialVersionUID = 2413321469771530086L;

	private int level;

	// firstEdge wird nicht mehr gebraucht, da jeder Knoten seine eigene
	// Adjazenzliste hat
	// private int firstEdge;

	private int firstLevelEdgeOffset;

	private int pqElement;

	private Edge[] edges;

	public UpNode() {
		this(0, 0, 0, new Edge[0]);
	}

	public UpNode(int l) {
		this(l, 0, 0, new Edge[0]);
	}

	public UpNode(int l, int fLEO, int pqE, Edge[] e) {
		level = l;
		// firstEdge = fE;
		firstLevelEdgeOffset = fLEO;
		pqElement = pqE;
		edges = e;
	}

	public int getLevel() {
		return level & (MAX_VALUE >> 1);
	}

	// public int getFirstEdge() {
	// return firstEdge;
	// }

	public int getFirstLevelEdge() {
		return getFirstLevelEdgeOffset();
	}

	// public int getLastEdge() {
	// return firstEdge + getLastEdgeOffset();
	// }

	private int getFirstLevelEdgeOffset() {
		return firstLevelEdgeOffset & LAST_EDGE_LIMIT;
	}

	// private int getLastEdgeOffset() {
	// return (firstLevelEdgeOffset & (LAST_EDGE_LIMIT << 16)) >>> 16;
	// }

	public int getPQElement() {
		return pqElement;
	}

	public boolean isTarget() {
		return (level & (MIN_VALUE >>> 1)) != 0;
	}

	public void setLevel(int l) {
		boolean t = isTarget();
		level = l;
		setTarget(t);
	}

	// public void setFirstEdge(int fE) {
	// firstEdge = fE;
	// }

	public void setFirstLevelEdge(int fLEO) {
		int offset = fLEO;
		assert (offset <= LAST_EDGE_LIMIT) : "firstLevelEdgeOffset out of bounds! : max. expected <"
				+ LAST_EDGE_LIMIT + "> but was <" + offset + ">";
		firstLevelEdgeOffset = (firstLevelEdgeOffset | offset)
				& (offset | (LAST_EDGE_LIMIT << 16));
	}

	public void setLastEdge(int lLE) {
		int offset = lLE;
		assert (offset <= LAST_EDGE_LIMIT) : "lastEdgeOffset out of bounds!";
		offset <<= 16;
		firstLevelEdgeOffset = (firstLevelEdgeOffset | offset)
				& (offset | (LAST_EDGE_LIMIT));
	}

	public void setPQElement(int pqE) {
		pqElement = pqE;
	}

	public void setTarget(boolean t) {
		if (t) {
			level = level | (MIN_VALUE >>> 1);
		} else {
			level = level & ~(MIN_VALUE >>> 1);
		}
	}

	public Edge edge(int index) {
		assert (index >= 0 && index < edges.length) : "UpNode: falscher Index für edge-Array";
		return edges[index];
	}

	public int lastEdge() {
		return edges.length;
	}

	@Override
	public String toString() {
		String res = "";
		res += "level: " + getLevel() + " ";
		res += "(" + getFirstLevelEdge() + "," + lastEdge() + ")";
		return res;
	}

	public void sortEdges() {
		Edge[] tmp = new Edge[lastEdge() - getFirstLevelEdge()];
		System.arraycopy(edges, getFirstLevelEdge(), tmp, 0, tmp.length);
		Arrays.sort(tmp);
		System.arraycopy(tmp, 0, edges, getFirstLevelEdge(), tmp.length);
	}

	public void swap(int edge1, int edge2) {
		Edge help = edges[edge1];
		edges[edge1] = edges[edge2];
		edges[edge2] = help;
	}

	public void addEdge(Edge edge, int index) {
		Edge[] tmp = new Edge[edges.length + 1];
		for (int i = 0; i < index; i++) {
			tmp[i] = edges[i];
		}
		tmp[index] = edge;
		for (int i = index; i < edges.length; i++) {
			tmp[i + 1] = edges[i];
		}
		edges = tmp;
	}

	public void removeEdge(int edgeIndex) {
		Edge[] tmp = new Edge[edges.length - 1];
		System.arraycopy(edges, 0, tmp, 0, edgeIndex);
		for (int i = edgeIndex + 1; i < edges.length; i++) {
			tmp[i - 1] = edges[i];
		}
		edges = tmp;
	}

	public void addEdge(Edge edge) {
		Edge[] tmp = new Edge[edges.length + 1];
		System.arraycopy(edges, 0, tmp, 0, edges.length);
		tmp[tmp.length - 1] = edge;
		edges = tmp;
	}

	public void deleteEdge(int e) {
		edges[e] = null;
	}

}